﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Caching.cs" company="Microsoft Corporation">
//   Copyright (c) Microsoft Corporation 2011.  All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace MultipleAppDomainTest
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.ServiceModel;

    /// <summary>
    /// The caching class. Represent the local caching.
    /// This class helps you to read and write local caching.
    /// There are 2 categories in local cashing folder -- All activity assemblies with their activity type children; All assemblies with their local location.
    /// </summary>
    public class Caching
    {
        /// <summary>
        /// Initializes static members of the <see cref="Caching"/> class.
        /// </summary>
        static Caching()
        {
            ActivityAssemblyItems = new ObservableCollection<ActivityAssemblyItem>();
            ActivityItems = new ObservableCollection<ActivityItem>();
        }

        /// <summary>
        /// Gets or sets all cached ActivityAssemblyItems.
        /// </summary>
        public static ObservableCollection<ActivityAssemblyItem> ActivityAssemblyItems
        {
            get { return activityAssemblyItems; } // explicitly use backing field so it doesn't get optimized away while debugging
            set { activityAssemblyItems = value; }
        }

        /// <summary>
        /// Backing field for property
        /// </summary>
        static ObservableCollection<ActivityAssemblyItem> activityAssemblyItems;

        /// <summary>
        /// Gets or sets all cached ActivityItems.
        /// </summary>
        public static ObservableCollection<ActivityItem> ActivityItems { get; set; }


        /// <summary>
        /// Try to retrieve a cached assembly
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="cachedAssembly"></param>
        /// <returns></returns>
        public static bool LoadCachedAssembly(AssemblyName assemblyName, out ActivityAssemblyItem cachedAssembly)
        {
            cachedAssembly = ActivityAssemblyItems.FirstOrDefault(assembly => assembly.Matches(assemblyName));
            return cachedAssembly != null;
        }

        /// <summary>
        /// The cache assembly.
        /// </summary>
        /// <param name="activityAssemblyItems">
        /// The activity assembly items.
        /// </param>
        public static void CacheAssembly(List<ActivityAssemblyItem> activityAssemblyItems, bool isFromServer = false)
        {
            if (activityAssemblyItems == null)
            {
                throw new ArgumentNullException("activityAssemblyItems");
            }

            if (activityAssemblyItems.Any(item => item == null))
            {
                throw new ArgumentNullException("activityAssemblyItems");
            }

            foreach (ActivityAssemblyItem assemblyItem in activityAssemblyItems)
            {
                
                // Check if a location is already in location catalog. If true, remove it first.
                ActivityAssemblyItem cachedAssembly;
                if (LoadCachedAssembly(assemblyItem.AssemblyName, out cachedAssembly))
                {
                    ActivityAssemblyItems.Remove(cachedAssembly);
                }

                // Copy assemblies to local caching directory
                string destFileName = Utility.CopyAssemblyToLocalCachingDirectory(assemblyItem.AssemblyName, assemblyItem.Location, false);
                // break link to original location by resetting Location and AssemblyName.CodeBase
                assemblyItem.Location = destFileName;
                assemblyItem.AssemblyName.CodeBase = null;
                assemblyItem.UpdateDateTime = DateTime.Now;
                
                if (isFromServer)
                {
                    var inspection = Utility.GetAssemblyInspectionService();
                    inspection.Inspect(destFileName);
                    assemblyItem.ActivityItems = inspection.SourceAssembly.ActivityItems;
                    assemblyItem.UserSelected = true;
                    assemblyItem.ActivityItems.ToList().ForEach(i =>
                    {
                        i.UserSelected = true;
                        i.Category = "Unassigned";
                    });
                }

                // Make ActivityItem read only. Note: ActivityItem's metadata can be edited only when imported.
                foreach (var activityItem in assemblyItem.ActivityItems)
                {
                    activityItem.IsReadOnly = true;
                }

                ActivityAssemblyItems.Add(assemblyItem);

            }

            // Regenerate ActivityAssemblyItems and ActivityItems
            Refresh();
        }

        /// <summary>
        /// Reload data from local caching categories.
        /// </summary>
        public static void Refresh()
        {
            // Serialize and save
            using (var stream = File.Open(Utility.GetActivityAssemblyCatalogFileName(), FileMode.Create))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, ActivityAssemblyItems.ToList());
            }

            Load();   // Reload from the local cache.
        }

        /// <summary>
        /// Load caching from local hard drive.
        /// </summary>
        public static void Load()
        {
            ActivityAssemblyItems.Clear();
            ActivityItems.Clear();

            if (File.Exists(Utility.GetActivityAssemblyCatalogFileName()))
            {
                var deserialized = Utility.DeserializeSavedContent(Utility.GetActivityAssemblyCatalogFileName()) as IEnumerable<ActivityAssemblyItem>;

                // Load assemblies
                deserialized.ToList().ForEach(item =>
                {
                    item.Assembly = Assembly.LoadFrom(item.Location);

                    // recompute if just-downloaded
                    if (null == item.NotSafeForTypeLoad)
                        item.Assembly.GetAsManyTypesAsPossible(owningAssemblyItem: item);

                    ActivityAssemblyItems.Add(item);   // Then, make them all available (ToolboxControlService will start picking them up)
                });

                ActivityAssemblyItems
                    .SelectMany(activityAssemblyItem => activityAssemblyItem.ActivityItems)
                    .ToList()
                    .ForEach(item => 
                    {
                        item.UserSelected = true;
                        item.IsReviewed = true;
                        ActivityItems.Add(item); 
                    });

            }
        }

    }
}
